﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace GameLogic {
	public enum EntityType {
		Empty,
		Rock,
		Lambda,
		ClosedLift,
		OpenLift,
		Earth,
		Start
	}

	public enum GameState {
		Running,
		Win,
		Lost,
		Aborted
	}

	public class Board {
		public readonly char[,] map;
		public readonly int CharX;
		public readonly int CharY;
		public readonly Board Prev = null;
		public readonly char Move;
		public readonly int Score = 0;
		public readonly int LambdaCount = 0;
		public readonly int LambdaCollected = 0;
		public readonly int Steps = 0;
		public readonly GameState State = GameState.Running;

		public int SizeX {
			get {
				return map.GetLength(0);
			}
		}

		public int SizeY {
			get {
				return map.GetLength(1);
			}
		}

		public Board(string text) {
			var lines = new List<string>(
				text.Split(new string[] {"\n"}, StringSplitOptions.RemoveEmptyEntries)
				);

			var sizeX = lines.Max(l => l.Length);
			var sizeY = lines.Count;

			map = new char[sizeX, sizeY];

			for (var y = 0; y < sizeY; y++)
				for (var x = 0; x < sizeX; x++) {
					map[x, y] = x < lines[sizeY - y - 1].Length ? lines[sizeY - y - 1][x] : ' ';
					if (map[x, y] == 'R') {
						CharX = x;
						CharY = y;
					}
					if (map[x, y] == '\\') {
						LambdaCount++;
					}
				}
		}

		public void Print() {
			for (var y = SizeY - 1; y >= 0; y--) {
				for (var x = 0; x < SizeX; x++) {
					Console.Write(map[x, y]);
				}
				Console.WriteLine();
			}
			Console.WriteLine("Score: " + this.Score);
			Console.WriteLine("Coord: {0}, {1}", this.CharX, this.CharY);
			Console.WriteLine("State: " + this.State);
			Console.WriteLine("Moves:");
			PrintMoves();
			Console.WriteLine();
		}

		public void PrintMoves() {
			var b = this;
			if (this.Prev != null) {
				Prev.PrintMoves();
				Console.Write(Move);
			}
		}

		public Board(Board start, char move) {
			int newX = start.CharX;
			int newY = start.CharY;		
			int dx = 0;
			int dy = 0;
			int lambdaCount = start.LambdaCount;
			int lambdaCollected = start.LambdaCollected;
			int score = start.Score;
			bool win = false;
			char[,] movedMap = (char[,])start.map.Clone();

			switch (move) {
				case 'L': dx = -1; break;
				case 'R': dx = 1; break;
				case 'U': dy = 1; break;
				case 'D': dy = -1; break;
				case 'A': break;
				case 'W': break;
				default: throw new Exception("invalid move");
			}

			var moveTo = start.map[newX + dx, newY + dy];
			var canMove = false;

			switch (moveTo) {
				case ' ': canMove = true; break;
				case '\\': canMove = true; lambdaCount--; lambdaCollected++; score += 25; break;
				case '.': canMove = true; break;
				case 'O': canMove = true; win = true;  break;
				case '*':
					if (dy == 0 && start.map[newX + dx * 2, newY] == ' ' ) {
						movedMap[newX + dx * 2, newY] = '*';
						canMove = true;
					}
					break;
			};

			if (canMove) {
				movedMap[newX, newY] = ' ';
				newX += dx;
				newY += dy;
				movedMap[newX, newY] = 'R';
			}

			var newMap = (char[,])movedMap.Clone();
			if (move != 'A') {
				// Map updates

				for (var y = 0; y < start.SizeY; y++) {
					for (var x = 0; x < start.SizeX; x++) {
						// drop down
						if (movedMap[x, y] == '*' && movedMap[x, y - 1] == ' ') {
							newMap[x, y] = ' ';
							newMap[x, y - 1] = '!';
						}
						// drop right
						else if (movedMap[x, y] == '*' && movedMap[x, y - 1] == '*' && movedMap[x + 1, y] == ' ' && movedMap[x + 1, y - 1] == ' ') {
							newMap[x, y] = ' ';
							newMap[x + 1, y - 1] = '!';
						}
						// drop left 
						else if (movedMap[x, y] == '*' && movedMap[x, y - 1] == '*'
							  && (movedMap[x + 1, y] != ' ' || movedMap[x + 1, y - 1] != ' ') && movedMap[x - 1, y] == ' ' && movedMap[x - 1, y - 1] == ' ') {
							newMap[x, y] = ' ';
							newMap[x - 1, y - 1] = '!';
						}
						// drop right from lambda
						else if (movedMap[x, y] == '*' && movedMap[x, y - 1] == '\\' && movedMap[x + 1, y] == ' ' && movedMap[x + 1, y - 1] == ' ') {
							newMap[x, y] = ' ';
							newMap[x + 1, y - 1] = '!';
						}
						// open exit
						else if (movedMap[x, y] == 'L' && lambdaCount == 0) {
							newMap[x, y] = 'O';
						}
						else {
							newMap[x, y] = movedMap[x, y];
						}
					}
				}
			score = score - 1;
			}

			this.map = newMap;
			this.CharX = newX;
			this.CharY = newY;
			this.LambdaCount = lambdaCount;
			this.LambdaCollected = lambdaCollected;
			this.Prev = start;
			this.Move = move;
			this.Score = score ;

			if (win) {
				this.State = GameState.Win;
				this.Score = this.Score + this.LambdaCollected * 50;
			} if (move == 'A') {
				this.State = GameState.Aborted;
				this.Score = this.Score + this.LambdaCollected * 25;
			}
			else if (newY < SizeX && newMap[newX, newY + 1] == '!') {
				State = GameState.Lost;
			}

			for (var y = 0; y < start.SizeY; y++) {
				for (var x = 0; x < start.SizeX; x++) {
					if (this.map[x, y] == '!') {
						this.map[x, y] = '*';
					}
				}
			}

		}

		
	}
}
